﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Soundelle.Oscillator
{
    public class GeneratorMulti : BaseDevice
    {

        public GeneratorMulti()
        {
        }

        #region "Parameters"

        private ParameterType[] _AvailableParameters = { 
            ParameterType.Parameter_DC_Offset,
            ParameterType.Parameter_Amplitude,
            ParameterType.Parameter_Frequency,
            ParameterType.Parameter_Phase,
            ParameterType.Parameter_Duty,
            ParameterType.Parameter_IsZeroPeak
        };
        override public ParameterType[] AvailableParameters
        {
            get
            { return _AvailableParameters; }
        }

        #endregion

        private DeviceFunctionType[] _AvailableFunctions = new DeviceFunctionType[] {
             DeviceFunctionType.Function_GeneratorSine,
             DeviceFunctionType.Function_GeneratorSquare,
             DeviceFunctionType.Function_GeneratorTriangle,
             DeviceFunctionType.Function_GeneratorRandom
        };
        override public DeviceFunctionType[] AvailableFunctions
        {
            get
            { return _AvailableFunctions; }
        }


        override protected void InitRmsRatio()
        {
            switch (Function)
            {
                case DeviceFunctionType.Function_GeneratorSine:
                    RmsRatio = Math.Sqrt(2.0) / 2.0;
                    break;

                case DeviceFunctionType.Function_GeneratorSquare:
                    if (Parameter_IsZeroPeak)
                        RmsRatio = Parameter_Duty;
                    else
                        RmsRatio = 1.0;
                    break;

                case DeviceFunctionType.Function_GeneratorTriangle:
                    RmsRatio = 0.5;
                    break;

                case DeviceFunctionType.Function_GeneratorRandom:
                    RmsRatio = 1.0 / Math.Pow((2.0 * Parameter_Distribution + 1.0), 2.0);
                    break;

                default:
                    break;
            }
        }

        override protected void InitFunction()
        {
            switch (Function)
            {
                case DeviceFunctionType.Function_GeneratorSine:
                    _AvailableParameters = new ParameterType[] { 
                        ParameterType.Parameter_DC_Offset,
                        ParameterType.Parameter_Amplitude,
                        ParameterType.Parameter_Frequency,
                        ParameterType.Parameter_Phase
                    };
                    break;

                case DeviceFunctionType.Function_GeneratorSquare:
                    _AvailableParameters = new ParameterType[] { 
                        ParameterType.Parameter_DC_Offset,
                        ParameterType.Parameter_Amplitude,
                        ParameterType.Parameter_Frequency,
                        ParameterType.Parameter_Phase,
                        ParameterType.Parameter_Duty,
                        ParameterType.Parameter_IsZeroPeak
                    };
                    break;

                case DeviceFunctionType.Function_GeneratorTriangle:
                    _AvailableParameters = new ParameterType[] { 
                        ParameterType.Parameter_DC_Offset,
                        ParameterType.Parameter_Amplitude,
                        ParameterType.Parameter_Frequency,
                        ParameterType.Parameter_Phase,
                        ParameterType.Parameter_Duty,
                        ParameterType.Parameter_IsZeroPeak
                    };
                    break;

                case DeviceFunctionType.Function_GeneratorRandom:
                    _AvailableParameters = new ParameterType[] { 
                        ParameterType.Parameter_DC_Offset,
                        ParameterType.Parameter_Amplitude,
                        ParameterType.Parameter_IsZeroPeak,
                        ParameterType.Parameter_Distribution,
                        ParameterType.Parameter_Averages
                    };
                    break;

                default:
                    break;
            }
        }

        override public void Reset()
        {
            string guidStr = GUID.ToString().Replace("-", "");
            int id = 0;
            for (int i = 0; i < 8; ++i)
            {
                id += Convert.ToInt32(guidStr.Substring(i * 4, 4), 16);
            }

            rnd = new Random(id);
            base.Reset();
        }

        private double prevFreq = 0.0;
        Random rnd = new Random();
        override public double SignalOut()
        {
            if (!Enabled)
                return 0.0;

            double signalOut = 0.0;
            double signalIn = 0.0;

            double DcOffset = Parameter_DC_Offset;
            double amplitude = Parameter_Amplitude;
            double frequency = Parameter_Frequency;
            double phase = Parameter_Phase;

            double duty = Parameter_Duty;

            double distribution = Parameter_Distribution;
            long averages = Parameter_Averages;

            DoModulationByChildDevices(ref signalOut, ref signalIn, ref amplitude, ref frequency, ref phase, ref duty);

            double actFreq = 0.0;
            if (TimePosition <= 0.0)
            {
                actFreq = 0.0;
            }
            else
            {
                actFreq = prevFreq + (frequency * TimePosition / SamplingRate) - (frequency * (TimePosition - 1.0) / SamplingRate);
            }

            double sig = 0.0;
            double sigPhaseRatio = 0.0;

            switch (Function)
            {
                case DeviceFunctionType.Function_GeneratorSine:
                    sig = amplitude * Math.Sin(2.0 * Math.PI * actFreq + phase);
                    break;

                case DeviceFunctionType.Function_GeneratorSquare:
                    sigPhaseRatio = actFreq + phase / (2.0 * Math.PI);
                    sigPhaseRatio = (sigPhaseRatio % 1.0);

                    while (sigPhaseRatio < 0.0)
                    {
                        sigPhaseRatio += 1.0;
                    }
                    while (sigPhaseRatio > 1.0)
                    {
                        sigPhaseRatio -= 1.0;
                    }

                    if (sigPhaseRatio <= duty)
                    {
                        sig = amplitude;
                    }
                    else
                    {
                        if (Parameter_IsZeroPeak)
                            sig = 0.0;
                        else
                            sig = -amplitude;
                    }
                    break;

                case DeviceFunctionType.Function_GeneratorTriangle:
                    sigPhaseRatio = actFreq + phase / (2.0 * Math.PI);
                    sigPhaseRatio = (sigPhaseRatio % 1.0);

                    while (sigPhaseRatio < 0.0)
                    {
                        sigPhaseRatio += 1.0;
                    }
                    while (sigPhaseRatio > 1.0)
                    {
                        sigPhaseRatio -= 1.0;
                    }

                    if (duty <= 0.0)
                        sig = amplitude * (1.0 - sigPhaseRatio);
                    else if (duty >= 1.0)
                        sig = amplitude * sigPhaseRatio;
                    else if (sigPhaseRatio < duty)
                        sig = amplitude * (sigPhaseRatio / duty);
                    else
                        sig = amplitude * (1.0 - (sigPhaseRatio - duty) / (1.0 - duty));

                    if (!Parameter_IsZeroPeak)
                        sig = 2 * sig - amplitude;

                    break;

                case DeviceFunctionType.Function_GeneratorRandom:
                    if (averages < 1)
                        averages = 1;

                    for (int i = 0; i < averages; ++i)
                    {
                        sig += amplitude * Math.Pow(rnd.NextDouble(), distribution);
                    }
                    sig /= averages;

                    if (!Parameter_IsZeroPeak)
                    {
                        int sign = rnd.Next(100);
                        if (sign < 50)
                            sig = -sig;
                    }
                    break;

                default:
                    break;
            }

            prevFreq = actFreq;

            ++TimePosition;
            double SignalOut = sig + amplitude * signalIn + signalOut + DcOffset;
            SetPreviousSignalOut(SignalOut);
            return SignalOut;
        }

    }
}
